package com.mate.cloud.auth.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import com.mate.cloud.auth.domain.MateUserDetails;
import com.mate.cloud.auth.domain.UmsAdmin;
import com.mate.cloud.auth.domain.UmsResource;
import com.mate.cloud.auth.dto.UpdateAdminPasswordParam;
import com.mate.cloud.auth.exception.AuthBusinessRuntimeException;
import com.mate.cloud.auth.exception.UserExceptionEnumCode;
import com.mate.cloud.auth.mapper.UmsAdminMapper;
import com.mate.cloud.auth.service.UmsAdminService;
import com.mate.cloud.auth.service.UmsResourceService;
import com.mate.cloud.auth.utils.SecurityUtils;
import com.mate.cloud.cache.service.RedisCacheService;
import com.mate.cloud.core.common.constant.CommonConstants;
import com.mate.cloud.core.util.JwtGeneratorUtil;
import com.mate.cloud.core.util.ObjectUtil;
import com.mate.cloud.core.util.StringUtils;
import com.mate.cloud.mybatis.bases.BasesServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.mate.cloud.auth.constants.CommonConstants.CLAIM_KEY_USERNAME;
import static com.mate.cloud.auth.constants.CommonConstants.TOKEN_USERNAME_ID;

/**
 * @description: java类作用描述
 * @author: MI
 * @email: 448341911@qq.com
 * @createTime: 2024/6/14 21:32
 * @updateUser: MI
 * @updateTime: 2024/6/14 21:32
 * @updateRemark: 修改内容
 * @version: v1.0
 */
@Slf4j
@Service
public class UmsAdminServiceImpl extends BasesServiceImpl<UmsAdminMapper, UmsAdmin> implements UmsAdminService {


    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private UmsResourceService umsResourceService;


    @Resource
    private RedisCacheService redisCacheService;


    @Resource
    private UmsAdminMapper umsAdminMapper;

    // 用于记录 token 及其刷新时间
    private static final Map<String, Long> tokenRefreshTimes = Maps.newConcurrentMap();

    private static final long REFRESH_INTERVAL = 1000 * 60 * 30; // 30 分钟

    @Override
    public UmsAdmin getAdminByUsername(String username) {
        UmsAdmin umsAdmin = getUmsAdmin(username);

        return umsAdmin;
    }

    /**
     * 根据用户名修改登录时间
     */
    private void updateLoginTimeByUsername(String username) {
        final UmsAdmin umsAdmin = getUmsAdmin(username);
        if (umsAdmin == null) return;

        umsAdmin.setLoginTime(new Date());
        umsAdminMapper.updateById(umsAdmin);
    }

    private UmsAdmin getUmsAdmin(String username) {
        LambdaQueryWrapper<UmsAdmin> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(UmsAdmin::getUsername, username);
        queryWrapper.eq(UmsAdmin::getStatus, 0);
        queryWrapper.last(CommonConstants.LIMIT_ONE);
        return umsAdminMapper.selectOne(queryWrapper);
    }

    @Override
    public String refreshToken(String paramToken) {
        if (paramToken == null || paramToken.isEmpty()) {
            log.warn("Token refresh failed: Token is empty");
            throw new AuthBusinessRuntimeException(UserExceptionEnumCode.TOKEN_NOT_EMPTY.getCode());
        }
        // token 截取
        String token = SecurityUtils.getToken(paramToken);

        if (StringUtils.isNotEmpty(token) && JwtGeneratorUtil.validateToken(token)) {
            return JwtGeneratorUtil.refreshAccessToken(token);
        } else {
            // 1、解析token
            DecodedJWT decodedJWT = JwtGeneratorUtil.decodeToken(token);
            if (decodedJWT != null) {
                // 如果token在30分钟之内刚刷新过，返回原token
//                final String newToken = tokenRefreshJustBefore(paramToken, refreshToken);
                final String username = decodedJWT.getSubject();
                return JwtGeneratorUtil.generateAccessToken(username);
            }
        }
        return null;
    }

    @Override
    public int updatePassword(UpdateAdminPasswordParam updatePasswordParam) {
        if (StrUtil.isEmpty(updatePasswordParam.getUsername()) || StrUtil.isEmpty(updatePasswordParam.getOldPassword()) || StrUtil.isEmpty(updatePasswordParam.getNewPassword())) {
            throw new AuthBusinessRuntimeException(UserExceptionEnumCode.PARAM_ERROR.getCode());
        }

        LambdaQueryWrapper<UmsAdmin> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(UmsAdmin::getUsername, updatePasswordParam.getUsername());
        List<UmsAdmin> adminList = umsAdminMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(adminList)) {
            throw new AuthBusinessRuntimeException(UserExceptionEnumCode.USER_ADMIN_NOT_EXITS.getCode());
        }
        UmsAdmin umsAdmin = adminList.get(0);
        if (!passwordEncoder.matches(updatePasswordParam.getOldPassword(), umsAdmin.getPassword())) {
            throw new AuthBusinessRuntimeException(UserExceptionEnumCode.OLD_PWD_ERROR.getCode());
        }
        umsAdmin.setPassword(passwordEncoder.encode(updatePasswordParam.getNewPassword()));

        UpdateWrapper<UmsAdmin> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(UmsAdmin::getPassword, umsAdmin.getPassword());
        umsAdminMapper.update(umsAdmin, updateWrapper);
//        adminCacheService.delAdmin(umsAdmin.getId());
        return 1;
    }

    @Override
    public String login(String username, String password) {
        String token = null;

        final MateUserDetails userDetails = this.loadUserByUsername(username);
        if (userDetails == null) {
            return token;
        }
        if (!passwordEncoder.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("密码不正确");
        }
        // 1 创建UsernamePasswordAuthenticationToken
        UsernamePasswordAuthenticationToken usernameAuthentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // 未认证的令牌进行认证
//            final Authentication authentication = this.authenticationManager.authenticate(usernameAuthentication);
        // 2 保存认证信息
        SecurityContextHolder.getContext().setAuthentication(usernameAuthentication);

//            Map<String, Object> dataMap = Maps.newHashMap();
//            dataMap.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        // 3 生成自定义token
        token = JwtGeneratorUtil.generateAccessToken(userDetails.getUsername());

        // 4、记录 token 的刷新时间
        tokenRefreshTimes.put(CLAIM_KEY_USERNAME, System.currentTimeMillis());
        // 5刷新用户登录时间
        updateLoginTimeByUsername(username);
        // 将 Token 存入 Redis 并设置过期时间
        final long expiration = (System.currentTimeMillis() + 30 * 60 * 1000);
        final String tokenKey = String.format(TOKEN_USERNAME_ID, userDetails.getUsername(), userDetails.getId());
        // 6、缓存token 到Redis缓存
        redisCacheService.set(tokenKey, token, expiration);
        // TODO 系统用户登录日志
        return token;
    }

    @Override
    public List<UmsResource> getResourceList(Integer adminId) {
        return umsResourceService.getResourceList(adminId);
    }

    @Override
    public MateUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 获取用户信息
        UmsAdmin umsAdmin = this.getAdminByUsername(username);
        if (ObjectUtil.isEmpty(umsAdmin)) {
            throw new UsernameNotFoundException("用户名或密码错误");
        }
        List<UmsResource> resourceList = getResourceList(umsAdmin.getId());
        return new MateUserDetails(umsAdmin.getId(), umsAdmin, resourceList);

    }
}
